package com.leetcode.cas;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * Created by huangyugen on 2019/07/29
 * CAS技术原理
 **/
public class TestAtomicInteger {

    /**
     * 2.1:cas是什么？

     cas的英文翻译全称是compare and set ，也就是比较替换技术，·它包含三个参数，CAS(V,E,N),其中V（variile）表示欲更新的变量，E(Excepted)表示预期的值，N(New)表示新值,只有当V等于E值的时候吗，才会将V的值设为N,如果V值和E值不同，则说明已经有其它线程对该值做了更新，则当前线程什么都不做，直接返回V值。

     举个例子，假如现在有一个变量int a=5;我想要把它更新为6，用cas的话，我有三个参数cas（5,5,6），我们要更新的值是5，找到了a=5，符合V值，预期的值也是5符合，然后就会把N=6更新给a,a的值就会变成6；

     2.2：cas的优点

     2.2.1cas是以乐观的态度运行的，它总是认为当前的线程可以完成操作，当多个线程同时使用CAS的时候只有一个最终会成功，而其他的都会失败。这种是由欲更新的值做的一个筛选机制，只有符合规则的线程才能顺利执行，而其他线程，均会失败，但是失败的线程并不会被挂起，仅仅是尝试失败，并且允许再次尝试（当然也可以主动放弃）

     2.2.2：cas可以发现其他线程的干扰，排除其他线程造成的数据污染

     产生的问题：
     ABA问题的根本在于cas在修改变量的时候，无法记录变量的状态，比如修改的次数，否修改过这个变量。
     这样就很容易在一个线程将A修改成B时，另一个线程又会把B修改成A,造成casd多次执行的问题。

     解决：
     AtomicStampReference

     * @param args
     */
    static AtomicStampedReference<Integer> money =new AtomicStampedReference<>(19,0);

    public static void main(String[] args) {
        AtomicInteger integer = new AtomicInteger(5);

//        boolean compareAndSet = integer.compareAndSet(2, 6);
//        boolean compareAndSet = integer.compareAndSet(5, 6);
//        int i = integer.get();
//
//        System.out.println("是否改变："+compareAndSet);
//        System.out.println("获取到的值："+i);

        for (int i = 0; i < 3; i++) {

            int stamp = money.getStamp();

            System.out.println("stamp的值是"+stamp);

            new Thread(){         //充值线程

                @Override
                public void run() {

                    while (true){
                        int timeStamp = money.getStamp();//1
                        Integer account = money.getReference();

                        if (account<20){

                            if (money.compareAndSet(account,account+20,timeStamp,timeStamp+1)){

                                System.out.println("余额小于20元，充值成功，目前余额："+money.getReference()+"元");
                                break;
                            }
                        }else {

//                            System.out.println("余额大于20元,无需充值");
                        }
                    }
                }
            }.start();
        }


        new Thread(){

            @Override
            public void run() {    //消费线程

                for (int j = 0; j < 100; j++) {

                    while (true){

                        int timeStamp = money.getStamp();//1

                        int currentMoney =money.getReference();//39

                        if (currentMoney>10){
                            System.out.println("当前账户余额大于10元");
                            if (money.compareAndSet(currentMoney,currentMoney-10,timeStamp,timeStamp+1)){

                                System.out.println("消费者成功消费10元，余额"+money.getReference());

                                break;
                            }
                        }else {
//                            System.out.println("没有足够的金额");

                            break;
                        }
                        try {
                            Thread.sleep(1000);
                        }catch (Exception ex){
                            ex.printStackTrace();
                            break;
                        }

                    }

                }
            }
        }.start();




    }
}
